#include "Map.h"

//----------------------------------------------------
Map::Map(Ogre::SceneManager* mSceneMgr, Ogre::String sHeightMapFileName)
{
	this->mSceneMgr = mSceneMgr;
	this->nWorldSize = 257;
	this->mWorldPoz = Ogre::Vector3(mWorldPoz.x, mWorldPoz.y, mWorldPoz.z);
	this->heightMapName = sHeightMapFileName;

    mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();

	mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mSceneMgr, Ogre::Terrain::ALIGN_X_Z, 257, 257);
    mTerrainGroup->setOrigin(Ogre::Vector3(0,0,0));

	configureTerrainDefaults();

	defineTerrain(sHeightMapFileName);

    mTerrainGroup->loadAllTerrains(true);

	initBlendMaps();

	mTerrainGroup->freeTemporaryResources();

	printf("\n%s","--------------------------------------------------");
	printf("\n%s","Terrain loaded...");
	printf("\n%s","--------------------------------------------------");
}
//----------------------------------------------------
Map::~Map()
{
    OGRE_DELETE mTerrainGroup;
    OGRE_DELETE mTerrainGlobals;
}

//---------------------------------------------------

Ogre::String Map::getHeightMapName(void)
{
	return heightMapName;
}

//---------------------------------------------------

Ogre::Real Map::getWorldSize(void)
{
	return nWorldSize;
}

//---------------------------------------------------

Ogre::Vector3 Map::getWorldPoz(void)
{
	return mWorldPoz;
}

//----------------------------------------------------
void Map::getTerrainImage(Ogre::String sImgFileName, bool bFlipX, bool bFlipY, Ogre::Image& mImg)
{
	    mImg.load(sImgFileName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    if (bFlipX)
        mImg.flipAroundY();
    if (bFlipY)
        mImg.flipAroundX();
}
//-----------------------------------------------------
void Map::defineTerrain(Ogre::String sHeightMapFileName)
{
	Ogre::Image mImg;
    getTerrainImage(sHeightMapFileName, true, true, mImg);
    mTerrainGroup->defineTerrain(0, 0, &mImg);
}

//------------------------------------------------------

void Map::changeTexture(int layerIndex, int size, Ogre::StringVector *textures)
{
	getTerrain()->replaceLayer(layerIndex,false,size,textures);
}

//------------------------------------------------------

Ogre::Terrain* Map::getTerrain(void)
{
	if(mTerrainGroup != 0)
	{
		return mTerrainGroup->getTerrain(0,0);
	}
	else
	{
		return 0;
	}
}

//------------------------------------------------------

Ogre::TerrainGlobalOptions* Map::getTerrainGlobalOptions(void)
{
	return mTerrainGlobals;
}

//------------------------------------------------------

Ogre::TerrainGroup* Map::getTerrainGroup(void)
{
	return mTerrainGroup;
}

//------------------------------------------------------
void Map::initBlendMaps(void)
{
	//default blend map
	initBlendMaps(8,0.6,8,5.1);
}
//------------------------------------------------------

void Map::initBlendMaps(Ogre::Real minHeight0, Ogre::Real fadeDist0, Ogre::Real minHeight1, Ogre::Real fadeDist1)
{
	if(getTerrain() != 0)
	{
		Ogre::TerrainLayerBlendMap* blendMap0 = getTerrain()->getLayerBlendMap(1);
		Ogre::TerrainLayerBlendMap* blendMap1 = getTerrain()->getLayerBlendMap(2);

		float* pBlend1 = blendMap1->getBlendPointer();
		for (Ogre::uint16 y = 0; y < getTerrain()->getLayerBlendMapSize(); ++y)
		{
			for (Ogre::uint16 x = 0; x < getTerrain()->getLayerBlendMapSize(); ++x)
			{
				Ogre::Real tx, ty;
 
				blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
				Ogre::Real height = getTerrain()->getHeightAtTerrainPosition(tx, ty);
				Ogre::Real val = (height - minHeight0) / fadeDist0;
				val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
 
				val = (height - minHeight1) / fadeDist1;
				val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
				*pBlend1++ = val;
			}
		}
		blendMap0->dirty();
		blendMap1->dirty();
		blendMap0->update();
		blendMap1->update();
	}
}

//------------------------------------------------------

void Map::configureTerrainDefaults(void)
{
	//default terrain layers
	configureTerrainDefaults(
							 "realgrass_diffusespecular.dds", 
							 "realgrass_normalheight.dds", 
							 "growth_weirdfungus-03_diffusespecular.dds", 
							 "growth_weirdfungus-03_normalheight.dds", 
							 "dirt_grayrocky_diffusespecular.dds", 
							 "dirt_grayrocky_normalheight.dds"
							 );

}

//------------------------------------------------------
void Map::configureTerrainDefaults(Ogre::String layer1Diffuse, Ogre::String layer1Normal, Ogre::String layer2Diffuse, Ogre::String layer2Normal, Ogre::String layer3Diffuse, Ogre::String layer3Normal)
{
	// Configure global
    mTerrainGlobals->setMaxPixelError(8);
    // testing composite map
    mTerrainGlobals->setCompositeMapDistance(3000);

	// Important to set these so that the terrain knows what to use for derived (non-realtime) data
    //mTerrainGlobals->setLightMapDirection(mLight->getDerivedDirection());
    mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());
    //mTerrainGlobals->setCompositeMapDiffuse(mLight->getDiffuseColour());

	// Configure default import settings for if we use imported image
    Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
    defaultimp.terrainSize = 256; //257
    defaultimp.worldSize = 257;
    defaultimp.inputScale = 25; //25
    defaultimp.minBatchSize = 33; //33
    defaultimp.maxBatchSize = 65; //65

	// textures
    defaultimp.layerList.resize(3);
    defaultimp.layerList[0].worldSize = 1;
	defaultimp.layerList[0].textureNames.clear();
    defaultimp.layerList[0].textureNames.push_back(layer1Diffuse);
    defaultimp.layerList[0].textureNames.push_back(layer1Normal);
    defaultimp.layerList[1].worldSize = 40;
	defaultimp.layerList[1].textureNames.clear();
    defaultimp.layerList[1].textureNames.push_back(layer2Diffuse);
    defaultimp.layerList[1].textureNames.push_back(layer2Normal);
    defaultimp.layerList[2].worldSize = 40;
	defaultimp.layerList[2].textureNames.clear();
    defaultimp.layerList[2].textureNames.push_back(layer3Diffuse);
    defaultimp.layerList[2].textureNames.push_back(layer3Normal);


}

//------------------------------------------------------

bool Map::editTerrain(const Ogre::Vector3& mCentrepos, Ogre::Real nTimeElapsed, Ogre::Real nBrushSizeTerrainSpace, bool bRaise)
{
	bool modified = false;

	if(getTerrain() != 0)
	{
		

		Ogre::Vector3 tsPos;
		getTerrain()->getTerrainPosition(mCentrepos, &tsPos);


		modified = true;

		Ogre::Real terrainSize = (getTerrain()->getSize() - 1);
		long startx = (tsPos.x - nBrushSizeTerrainSpace) * terrainSize;
		long starty = (tsPos.y - nBrushSizeTerrainSpace) * terrainSize;
		long endx = (tsPos.x + nBrushSizeTerrainSpace) * terrainSize;
		long endy= (tsPos.y + nBrushSizeTerrainSpace) * terrainSize;
		startx = std::max(startx, 0L);
		starty = std::max(starty, 0L);
		endx = std::min(endx, (long)terrainSize);
		endy = std::min(endy, (long)terrainSize);
		for (long y = starty; y <= endy; ++y)
		{
			for (long x = startx; x <= endx; ++x)
			{
				Ogre::Real tsXdist = (x / terrainSize) - tsPos.x;
				Ogre::Real tsYdist = (y / terrainSize)  - tsPos.y;

				Ogre::Real weight = std::min((Ogre::Real)1.0, 
				Ogre::Math::Sqrt(tsYdist * tsYdist + tsXdist * tsXdist) / Ogre::Real(0.5 * nBrushSizeTerrainSpace));

				weight = 1.0 - (weight * weight);

				float addedHeight = weight * 10.0 * nTimeElapsed;
				float newheight;

				if (bRaise)
				{
					newheight = getTerrain()->getHeightAtPoint(x, y) + addedHeight;
				}
				else
				{
					newheight = getTerrain()->getHeightAtPoint(x, y) - addedHeight;
				}

				if(newheight < 0)
				{
					newheight = 0;
				}

				if(newheight > 25)
				{
					newheight = 25;
				}

				getTerrain()->setHeightAtPoint(x, y, newheight);

			}
		}

	}
	return modified;
}

//------------------------------------------------------

void Map::generateHeightMap(Ogre::String fileName)
{
	int terrainSize = getTerrain()->getSize()-1;

	Ogre::Real oldMin = 999999;
	Ogre::Real oldMax = 0;

	Ogre::Real newMin = 0;
	Ogre::Real newMax = 255;
	Ogre::Real temp;

	BMP Output;
	Output.SetSize( 256 , 256 );
	Output.SetBitDepth( 24 );

	for(int i=0;i<terrainSize;i++)
	{
		for(int j=0;j<terrainSize;j++)
		{

				temp = getTerrain()->getHeightAtPoint(i,j);

				if(temp>oldMax)
				{
					oldMax = temp;
				}

				if(temp<oldMin)
				{
					oldMin = temp;
				}
		}
	}

	std::cout << "\n<HeightMapGenerator>: min: " << oldMin << "   max: " << oldMax;

	int newI;
	int newJ;

	for(int i=0;i<terrainSize;i++)
	{
		for(int j=0;j<terrainSize;j++)
		{
				Ogre::Real oldValue = getTerrain()->getHeightAtPoint(i,j);
				
				int newValue = ceil(     (((oldValue - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin   );

				newI = (terrainSize-1)-i;
				newJ = (terrainSize-1)-j;

				Output(newI,j)->Red = newValue;
				Output(newI,j)->Green = newValue;
				Output(newI,j)->Blue = newValue;
		}
	}

	Ogre::String fullName = "../../media/sceneview/terrain/";
	fullName.append(fileName);

	Output.WriteToFile(fullName.data());
}


//------------------------------------------------------

void Map::rebuildTerrain(Ogre::String sHeightMapFileName)
{
	std::cout << "\n<Map>: rebuilding terrain with heightmap: " << sHeightMapFileName.data();
	this->heightMapName = sHeightMapFileName;
	configureTerrainDefaults(
							 "realgrass_diffusespecular.dds", 
							 "realgrass_normalheight.dds", 
							 "growth_weirdfungus-03_diffusespecular.dds", 
							 "growth_weirdfungus-03_normalheight.dds", 
							 "dirt_grayrocky_diffusespecular.dds", 
							 "dirt_grayrocky_normalheight.dds"
							 );
	defineTerrain(sHeightMapFileName);
	getTerrainGroup()->loadAllTerrains(true);
	initBlendMaps();
	getTerrainGroup()->freeTemporaryResources();
}

//------------------------------------------------------